/*
 * Copyright (C) 2012 Google Inc.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Controls the anymoteSession object.  The anymoteSession object is created by
 * the googletvremote plugin during initialization.  The Anymote session object
 * is the communication mechanism with a paired device.  Before you can use the
 * Anymote session you need to pair with a Google TV device using a Pairing
 * session.  Anymote session functions include:
 *     startSession(name, version, host, port, callback)
 *     stopSession()
 *     sendKeyEvent(keycode, action)
 *     sendMouseMove(xDelta, yDelta)
 *     sendMouseWheel(xScroll, yScroll)
 *     sendDatafunction(type, data)
 *     sendFling(uri, sequence)
 *     sendPing()
 */


/** Anymote session object that this controller manages. */
var anymoteSession;


/** Array of ip addresses loaded from localStorage that have already paired. */
var pairedDevices = [];


/** Index used when traversing through the list of paired devices. */
var deviceIndex = 0;


/**
 * Start an Anymote session to communicate with Google TV. Before starting an
 * Anymote session, the Google TV at this ip address must already be paired.
 * @param {string} googleTvIpAddress IP address of the Google TV.
 * @param {function} response Callback function that will be called when the
 *     starting the Anymote session is successful or fails.
 */
var anymoteStartSession = function(googleTvIpAddress, response) {
  // DONE: Set a timeout that will trigger if there is no reply from the TV.
  var anymoteSessionTimeout = window.setTimeout(function() {
    displayToast('Timeout starting Anymote session with ' + googleTvIpAddress);
    document.body.className = '';
    response({type: googletvremote.anymote.EventType.ERROR});
  }, 2000);

  // DONE: Stop the old session if one is already running.
  anymoteSession.stopSession();  // No harm if there is no active connection.

  // DONE: Start an Anymote session.
  // DONE: Listen for Anymote events from the TV.  For example,
  //  googletvremote.anymote.EventType.CONNECTED save the device
  //  googletvremote.anymote.EventType.ACK print a message
  //  googletvremote.anymote.EventType.ERROR send error response to callback
  anymoteSession.startSession('Anymote Example', 1, googleTvIpAddress,
      9551, // Note, assuming the Anymote port is 9551, but in a real app you
      // should use the servicePort returned by the discovery client.
      function(e) {
        // An Anymote event parameter will have the following properties:
        //   type - The type of the event (by far the most important property).
        //   success - If the event was a success.
        //   sequence - The sequence number used to identify this transaction.
        //   dataType - Data mime type used in DATA events.
        //   dataValue - Data value used in DATA events.
        //   errorCode - Detailed description used in ERROR events.
        // type and errorCode constants found in googletvremote_constants.js
        switch (e.type) {
          case googletvremote.anymote.EventType.INVALID:
            console.log('Received Anymote session event... INVALID');
            break;
          case googletvremote.anymote.EventType.CONNECTED:
            console.log('Received Anymote session event... CONNECTED');
            window.clearTimeout(anymoteSessionTimeout);
            document.body.className = 'connected'; // Turns popup body green.
            displayToast('Successfully started Anymote session with ' +
                googleTvIpAddress);
            // Make sure this device is at the top of the paired device list.
            pairedDevices = [];
            var devicesInStorage =
                localStorage.getItem(STORAGE_KEY_PAIRED_DEVICES);
            if (devicesInStorage) {
              pairedDevices = JSON.parse(devicesInStorage);
            }
            var indexOfDevice = pairedDevices.indexOf(googleTvIpAddress);
            if (indexOfDevice == -1) {
              // Somehow this device was missing.  Add it to the list.
              pairedDevices.unshift(googleTvIpAddress);  // Adds to beginning.
              localStorage.setItem(STORAGE_KEY_PAIRED_DEVICES,
                  JSON.stringify(pairedDevices));
              updatePairedDevicesList();
            } else if (indexOfDevice != 0) {
              // Move this device into the first slot.
              pairedDevices.splice(indexOfDevice, 1); // Remove.
              pairedDevices.unshift(googleTvIpAddress); // Add to beginning.
              localStorage.setItem(STORAGE_KEY_PAIRED_DEVICES,
                  JSON.stringify(pairedDevices));
              updatePairedDevicesList();
            }
            response({type: googletvremote.anymote.EventType.CONNECTED});
            break;
          case googletvremote.anymote.EventType.ACK:
            console.log('Received Anymote session event... ACK');
            displayToast('Ping ACK.'); // Response to sendPing.
            break;
          case googletvremote.anymote.EventType.DATA:
            console.log('Received Anymote session event... DATA');
            break;
          case googletvremote.anymote.EventType.DATALIST:
            console.log('Received Anymote session event... DATALIST');
            break;
          case googletvremote.anymote.EventType.FLINGRESULT:
            console.log('Received Anymote session event... FLINGRESULT');
            if (e.success) {
              console.log('Successful fling for sequence = ' + e.sequence);
              displayToast('Fling result successful');
            }
            break;
          case googletvremote.anymote.EventType.ERROR:
            console.log('Received Anymote session event... ERROR');
            displayToast('Anymote error ' +
                googletvremote.anymote.ErrorCodeStrings[e.errorCode] +
                ' with ' + googleTvIpAddress);
            console.log('Anymote error ' +
                googletvremote.anymote.ErrorCodeStrings[e.errorCode] +
                ' with ' + googleTvIpAddress);
            if (e.errorCode == googletvremote.anymote.ErrorCode.NOTPAIRED) {
              // Note, often you don't get an ERROR just a timeout.
              window.clearTimeout(anymoteSessionTimeout);
              document.body.className = '';
              response({type: googletvremote.anymote.EventType.ERROR});
            }
            break;
        }
      });
};


/** Go through the paired device list and attempt to start Anymote sessions. */
var anymoteConnectToExistingDevice = function() {
  // DONE: Pull the paired device list from localStorage
  var devicesInStorage = localStorage.getItem(STORAGE_KEY_PAIRED_DEVICES);
  if (devicesInStorage) {
    deviceIndex = 0; // Try the first paired device then move through the list.
    pairedDevices = JSON.parse(devicesInStorage);
    var googleTvIpAddress = pairedDevices[deviceIndex];
    // DONE: Attempt to pair with the device in index 0
    // DONE: Set a callback that will try the next device if this one fails.
    anymoteStartSession(googleTvIpAddress,
        anymoteExistingDeviceResponseHandler);
  } else {
    // displayToast('No devices have been paired yet');
  }
};


/**
 * Calls the next device in the list if this device failed.
 * @param {Object} e Event indicating if the start session event was successful.
 */
var anymoteExistingDeviceResponseHandler = function(e) {
  // If the type is googletvremote.anymote.EventType.CONNECTED you are done.
  // DONE: Otherwise attempt to connect using the next device in the array.
  if (e.type == googletvremote.anymote.EventType.CONNECTED) {
    deviceIndex = -1; // Reset index. Well done on your successful connection.
    return;
  } else {
    // Try the next device in the list if there are more.
    deviceIndex++;
    if (deviceIndex < pairedDevices.length) {
      console.log('Device ' + pairedDevices[deviceIndex - 1] + ' failed. ' +
          'Attempting to connect to device ' + pairedDevices[deviceIndex]);
      anymoteStartSession(pairedDevices[deviceIndex],
          anymoteExistingDeviceResponseHandler);
    } else {
      displayToast('Unable to establish an existing connection');
    }
  }
};


/**
 * Sends an Anymote key event to Google TV.  See the googletvremote_constants
 * for a complete list of keycode values.
 * @param {number} keycode Anymote keycode value to send to the TV.
 */
var sendAnymoteKeyEvent = function(keycode) {
  anymoteSession.sendKeyEvent(keycode, googletvremote.anymote.Action.DOWN);
  anymoteSession.sendKeyEvent(keycode, googletvremote.anymote.Action.UP);
};


/**
 * Sends a data message of type STRING.
 * @param {string} message String to send to the TV.
 */
var sendAnymoteStringMessage = function(message) {
  anymoteSession.sendData(googletvremote.anymote.DataType.STRING, message);
};


/**
 * Flings a uri to the Google TV.
 * @param {string} uri Uri to send to the Google TV.  This uri will be used as
 *     the data property in an Android Intent sent to Google TV.
 * @see http://developer.android.com/guide/topics/intents/intents-filters.html
 * @param {number=} sequenceNumber Optional sequence number that will be used in
 *     the Google TV reply to this message.  Not critical but must be > 0.
 */
var sendAnymoteFling = function(uri, sequenceNumber) {
  sequenceNumber = sequenceNumber || 42; // Sequence number default if not given
  anymoteSession.sendFling(uri, sequenceNumber);
};


/** Sends a ping to the Google TV. Used to test connection status. */
var sendAnymotePing = function() {
  anymoteSession.sendPing();
  // In a real app you would listen for an ACK to determine if still connected.
  // In this demo there is a simple message when the ACK is received.
};
